package main.java;
import java.io.*;
import java.util.regex.*; 
import java.util.ArrayList;
import java.util.HashMap;


public class GraphicsUnit {
	/**
	 * @author: William Cheng
	 * July 3rd, 2012
	 */
	//coordinate data field
	private ArrayList<ArrayList<Object>> BusLineList;	
	private ArrayList<ArrayList<Object>> LineList;
	private ArrayList<ArrayList<Object>> CircleUnfilledList;
	private ArrayList<ArrayList<Object>> CircleFilledList;
	private ArrayList<ArrayList<Object>> RectangleFilledList;
	private ArrayList<ArrayList<Object>> TriangleFilledList;
	private ArrayList<ArrayList<Object>> TextList;
	private HashMap<String, ArrayList<ArrayList<Object>>> CanvasMap;
	private static String[] CanvasType = {"Bus","Line","CircleU","Circle","Rect", "Tri", "Text"};
	//constant related to canvas
	final public static int CANVAS_WIDTH = 1200;
	final public static int CANVAS_HEIGHT = 600;
	final public static int R_TRANSFORMER = 20;
	final public static int BREAKER_LENGTH = 14;
	final public static int R_DISCONNECTOR = 4;
	final public static int BUS_LINE_WIDTH = 3;
	final public static int DEFAULT_LINE_WIDTH = 1;
	final public static int TITLE_FONTSIZE = 30;
	final public static int DEFAULT_FONTSIZE = 10;
	final public static int TRI_BASE_LENGTH = 4;
	final public static int TRI_HEIGHT = 15;
	
	public GraphicsUnit(){			//constructor
		BusLineList = new ArrayList<ArrayList<Object>>();
		LineList = new ArrayList<ArrayList<Object>>();
		CircleUnfilledList = new ArrayList<ArrayList<Object>>();
		CircleFilledList = new ArrayList<ArrayList<Object>>();
		RectangleFilledList = new ArrayList<ArrayList<Object>>();
		TriangleFilledList = new ArrayList<ArrayList<Object>>();
		TextList = new ArrayList<ArrayList<Object>>();
		CanvasMap = new HashMap<String, ArrayList<ArrayList<Object>>>();
	}
	
	private void generateCanvasMap(){
		CanvasMap.clear();
		CanvasMap.put(CanvasType[0], BusLineList);
		CanvasMap.put(CanvasType[1], LineList);
		CanvasMap.put(CanvasType[2], CircleUnfilledList);
		CanvasMap.put(CanvasType[3], CircleFilledList);
		CanvasMap.put(CanvasType[4], RectangleFilledList);
		CanvasMap.put(CanvasType[5], TriangleFilledList);
		CanvasMap.put(CanvasType[6], TextList);
	}
	/*
	public static void main(String[] args) throws IOException{
		// TODO Auto-generated method stub
		GraphicsUnit g = new GraphicsUnit();
		ArrayList<ArrayList<Object>> coordinateMap;
		LayoutController layout = new LayoutController();
		coordinateMap = layout.getDefaultcoordinateMap();
		
		g.sortByCanvasType(coordinateMap);
		g.HTML5gen(g.CanvasMap);
		//System.out.println(g.CanvasMap);
		
		/*
		String[][] temp;
		temp = g.list2String(g.BusLineList);
		System.out.println(Arrays.deepToString(temp));
		
	}
*/

	//add equipment to specific elements of canvas
	private void addBus(ArrayList<Object> equip){
		ArrayList<Object> equipTemp = new ArrayList<Object>();
		equipTemp.clear();
		equipTemp.addAll(equip);
		equipTemp.set(1, (int) Math.floor( ((Double) equip.get(1)) * CANVAS_WIDTH)  );
		equipTemp.set(2, (int) Math.floor(((Double) equip.get(2)) * CANVAS_HEIGHT));
		equipTemp.set(3, (int) Math.floor(((Double) equip.get(3)) * CANVAS_WIDTH));
		equipTemp.set(4, (int) Math.floor(((Double) equip.get(4)) * CANVAS_HEIGHT));
		//...........line width		
		BusLineList.add(equipTemp);
	}
	
	private void addBreaker(ArrayList<Object> equip){
		ArrayList<Object> equipTemp = new ArrayList<Object>();
		equipTemp.clear();
		equipTemp.addAll(equip);
		equipTemp.set(1, (int) Math.floor(((Double) equip.get(1)) * CANVAS_WIDTH));
		equipTemp.set(2, (int) Math.floor(((Double) equip.get(2)) * CANVAS_HEIGHT));
		RectangleFilledList.add(equipTemp);
	}
	
	private void addDisConnector(ArrayList<Object> equip){
		ArrayList<Object> equipTemp = new ArrayList<Object>();
		equipTemp.clear();
		equipTemp.addAll(equip);
		equipTemp.set(1, (int) Math.floor(((Double) equip.get(1)) * CANVAS_WIDTH));
		equipTemp.set(2, (int) Math.floor(((Double) equip.get(2)) * CANVAS_HEIGHT));
		CircleFilledList.add(equipTemp);
	}
	
	private void addLine(ArrayList<Object> equip){
		ArrayList<Object> equipTemp = new ArrayList<Object>();
		equipTemp.clear();
		equipTemp.addAll(equip);
		equipTemp.set(1, (int) Math.floor(((Double) equip.get(1)) * CANVAS_WIDTH));
		equipTemp.set(2, (int) Math.floor(((Double) equip.get(2)) * CANVAS_HEIGHT));
		equipTemp.set(3, (int) Math.floor(((Double) equip.get(3)) * CANVAS_WIDTH));
		equipTemp.set(4, (int) Math.floor(((Double) equip.get(4)) * CANVAS_HEIGHT));
		LineList.add(equipTemp);
	}
	
	private void addTransformer(ArrayList<Object> equip){
		ArrayList<Object> equipTemp = new ArrayList<Object>();
		equipTemp.clear();
		equipTemp.addAll(equip);
		equipTemp.set(1, (int) Math.floor(((Double) equip.get(1)) * CANVAS_WIDTH));
		equipTemp.set(2, (int) Math.floor(((Double) equip.get(2)) * CANVAS_HEIGHT));
		CircleUnfilledList.add(equipTemp);
	}
	
	private void addSynchronousMachine(ArrayList<Object> equip){
		ArrayList<Object> equipTemp = new ArrayList<Object>();
		equipTemp.clear();
		equipTemp.addAll(equip);
		equipTemp.set(1, (int) Math.floor(((Double) equip.get(1)) * CANVAS_WIDTH));
		equipTemp.set(2, (int) Math.floor(((Double) equip.get(2)) * CANVAS_HEIGHT));
		CircleUnfilledList.add(equipTemp);
		equipTemp.add("G");
		TextList.add(equipTemp);
	}
	
	private void addACLine(ArrayList<Object> equip){
		ArrayList<Object> equipTemp = new ArrayList<Object>();
		equipTemp.clear();
		equipTemp.addAll(equip);
		equipTemp.set(1, (int) Math.floor(((Double) equip.get(1)) * CANVAS_WIDTH));
		equipTemp.set(2, (int) Math.floor(((Double) equip.get(2)) * CANVAS_HEIGHT));
		TriangleFilledList.add(equipTemp);
	}
	
	private void addText(ArrayList<Object> equip){
		ArrayList<Object> equipTemp = new ArrayList<Object>();
		equipTemp.clear();
		equipTemp.addAll(equip);
		equipTemp.set(1, (int) Math.floor(((Double) equip.get(1)) * CANVAS_WIDTH));
		equipTemp.set(2, (int) Math.floor(((Double) equip.get(2)) * CANVAS_HEIGHT));
		TextList.add(equipTemp);
	}
	
	//sort equipments by canvas type, then translate them into specific coordinate data
	public void sortByCanvasType(ArrayList<ArrayList<Object>> coordinateMap){
		for (ArrayList<Object> equip:coordinateMap){
			if (equip.get(0) == "Bus"){
				addBus(equip);
			}
			else if (equip.get(0) == "Breaker"){
				addBreaker(equip);
			}
			else if (equip.get(0) == "DisConnector"){
				addDisConnector(equip);
			}
			else if (equip.get(0) == "Line"){
				addLine(equip);
			}
			else if (equip.get(0) == "Transformer"){
				addTransformer(equip);
			}	
			else if (equip.get(0) == "Syn"){		//Synchronous Machine
				addSynchronousMachine(equip);
			}
			else if (equip.get(0) == "ACLine"){		//AC line
				addACLine(equip);
			}
			else if (equip.get(0) == "Text" || equip.get(0) == "Title"){
				addText(equip);
			}
			else{
				System.out.println("Equipment type not found!\n");
			}
		}
		generateCanvasMap();
	}
	
	//transform all elements in a list into strings
	public static String[][] list2String(ArrayList<ArrayList<Object>> list){
		if (list.isEmpty()){
			System.out.println("Original list is empty!");
			return null;
		}
		else{
			String strArray[][] = new String[list.size()][];
			for (int n=0; n<list.size(); n++){
				strArray[n] = new String[list.get(n).size()];
				for (int m = 0; m<list.get(n).size(); m++)
					strArray[n][m] = list.get(n).get(m).toString();
			}
			return strArray;
		}
	}
	
	//write coordinate data into javascript
	private void writeScriptData(BufferedWriter script/*, HashMap<String, ArrayList<ArrayList<Object>>> CanvasMap*/) throws IOException{
		script.write("var BusLine = new Array();\n");
		script.write("var Line = new Array();\n");
		script.write("var CircleU = new Array();\n");
		script.write("var Circle = new Array();\n");
		script.write("var Rect = new Array();\n");
		script.write("var Tri = new Array();\n");
		script.write("var Text = new Array();\n");
		writeBusLine(script, CanvasMap.get(CanvasType[0]));
		writeLine(script, CanvasMap.get(CanvasType[1]));
		writeCircleU(script, CanvasMap.get(CanvasType[2]));
		writeCircle(script, CanvasMap.get(CanvasType[3]));
		writeRect(script, CanvasMap.get(CanvasType[4]));
		writeTri(script, CanvasMap.get(CanvasType[5]));
		writeText(script, CanvasMap.get(CanvasType[6]));
		script.write("const R_TRANSFORMER = " + R_TRANSFORMER + ";\n");
		script.write("const BREAKER_LENGTH = " + BREAKER_LENGTH + ";\n");
		script.write("const R_DISCONNECTOR = " + R_DISCONNECTOR + ";\n");
		script.write("const BUS_LINE_WIDTH = " + BUS_LINE_WIDTH + ";\n");
		script.write("const DEFAULT_LINE_WIDTH = " + DEFAULT_LINE_WIDTH + ";\n");
		script.write("const G_FONTSIZE  = " + R_TRANSFORMER + ";\n");		
		script.write("const DEFAULT_FONTSIZE  = " + DEFAULT_FONTSIZE + ";\n");
		script.write("const TITLE_FONTSIZE = " + TITLE_FONTSIZE +";\n");
		script.write("const TRI_BASE_LENGTH = " + TRI_BASE_LENGTH + ";\n");	
		script.write("const TRI_HEIGHT = " + TRI_HEIGHT + ";\n");	
	}
	
	private void writeBusLine(BufferedWriter script, ArrayList<ArrayList<Object>> BusLineList) throws IOException{
		if (!BusLineList.isEmpty()){		//not empty, then write
			String temp;
			try{
				
				for (int i=0; i<BusLineList.size(); i++){
					script.write("BusLine["+i+"] = new Array();\n");
					for (int j =0; j<BusLineList.get(i).size(); j++){
						//if not numeric, add extra ""
						temp = BusLineList.get(i).get(j).toString();
						if (isNumeric(temp))
							script.write("BusLine["+i+"]["+j+"] = "+ temp +";\n");
						else
							script.write("BusLine["+i+"]["+j+"] = \""+ temp +"\";\n");
					}
				}
			} catch(IOException e){
				System.out.println("BusLine IO exception!\n");
			}
		}
	}
	
	private void writeLine(BufferedWriter script, ArrayList<ArrayList<Object>> LineList) throws IOException{
		if (!LineList.isEmpty()){		//not empty, then write
			String temp;
			try{
				
				for (int i=0; i<LineList.size(); i++){
					script.write("Line["+i+"] = new Array();\n");
					for (int j =0; j<LineList.get(i).size(); j++){
						//if not numeric, add extra ""
						temp = LineList.get(i).get(j).toString();
						if (isNumeric(temp))
							script.write("Line["+i+"]["+j+"] = "+ temp +";\n");
						else
							script.write("Line["+i+"]["+j+"] = \""+ temp +"\";\n");
					}
				}
			} catch(IOException e){
				System.out.println("Line IO exception!\n");
			}
		}
	}

	private void writeCircleU(BufferedWriter script, ArrayList<ArrayList<Object>> CircleUnfilledList) throws IOException{
		if (!CircleUnfilledList.isEmpty()){		//not empty, then write
			String temp;
			try{
				
				for (int i=0; i<CircleUnfilledList.size(); i++){
					script.write("CircleU["+i+"] = new Array();\n");
					for (int j =0; j<CircleUnfilledList.get(i).size(); j++){
						//if not numeric, add extra ""
						temp = CircleUnfilledList.get(i).get(j).toString();
						if (isNumeric(temp))
							script.write("CircleU["+i+"]["+j+"] = "+ temp +";\n");
						else
							script.write("CircleU["+i+"]["+j+"] = \""+ temp +"\";\n");
					}
				}
			} catch(IOException e){
				System.out.println("CircleU IO exception!\n");
			}
		}
	}
	
	private void writeCircle(BufferedWriter script, ArrayList<ArrayList<Object>> CircleFilledList) throws IOException{
		if (!CircleFilledList.isEmpty()){		//not empty, then write
			String temp;
			try{
				
				for (int i=0; i<CircleFilledList.size(); i++){
					script.write("Circle["+i+"] = new Array();\n");
					for (int j =0; j<CircleFilledList.get(i).size(); j++){
						//if not numeric, add extra ""
						temp = CircleFilledList.get(i).get(j).toString();
						if (isNumeric(temp))
							script.write("Circle["+i+"]["+j+"] = "+ temp +";\n");
						else
							script.write("Circle["+i+"]["+j+"] = \""+ temp +"\";\n");
					}
				}
			} catch(IOException e){
				System.out.println("Circle IO exception!\n");
			}
		}
	}
	
	private void writeRect(BufferedWriter script, ArrayList<ArrayList<Object>> RectangleFilledList) throws IOException{
		if (!RectangleFilledList.isEmpty()){		//not empty, then write
			String temp;
			try{
				
				for (int i=0; i<RectangleFilledList.size(); i++){
					script.write("Rect["+i+"] = new Array();\n");
					for (int j =0; j<RectangleFilledList.get(i).size(); j++){
						//if not numeric, add extra ""
						temp = RectangleFilledList.get(i).get(j).toString();
						if (isNumeric(temp))
							script.write("Rect["+i+"]["+j+"] = "+ temp +";\n");
						else
							script.write("Rect["+i+"]["+j+"] = \""+ temp +"\";\n");
					}
				}
			} catch(IOException e){
				System.out.println("Rect IO exception!\n");
			}
		}
	}
	
	private void writeTri(BufferedWriter script, ArrayList<ArrayList<Object>> TriangleFilledList) throws IOException{
		if (!TriangleFilledList.isEmpty()){		//not empty, then write
			String temp;
			try{
				
				for (int i=0; i<TriangleFilledList.size(); i++){
					script.write("Tri["+i+"] = new Array();\n");
					for (int j =0; j<TriangleFilledList.get(i).size(); j++){
						//if not numeric, add extra ""
						temp = TriangleFilledList.get(i).get(j).toString();
						if (isNumeric(temp))
							script.write("Tri["+i+"]["+j+"] = "+ temp +";\n");
						else
							script.write("Tri["+i+"]["+j+"] = \""+ temp +"\";\n");
					}
				}
			} catch(IOException e){
				System.out.println("Tri IO exception!\n");
			}
		}
	}
	
	private void writeText(BufferedWriter script, ArrayList<ArrayList<Object>> TextList) throws IOException{
		if (!TextList.isEmpty()){		//not empty, then write
			String temp;
			try{
				
				for (int i=0; i<TextList.size(); i++){
					script.write("Text["+i+"] = new Array();\n");
					for (int j =0; j<TextList.get(i).size(); j++){
						//if not numeric, add extra ""
						temp = TextList.get(i).get(j).toString();
						if (isNumeric(temp))
							script.write("Text["+i+"]["+j+"] = "+ temp +";\n");
						else
							script.write("Text["+i+"]["+j+"] = \""+ temp +"\";\n");
					}
				}
			} catch(IOException e){
				System.out.println("Text IO exception!\n");
			}
		}
	}
	
	//output data to javascript, then run index.html
	public void HTML5gen(/*HashMap<String, ArrayList<ArrayList<Object>>> CanvasMap*/){
		output2js();
		//run html in OS
		try{
			Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler index.html");
		} catch(IOException e){
			System.out.println("execute index.html error!");
		}
		
	}
	
	//judge whether the string is numeric 
	private boolean isNumeric(String str){ 
	    Pattern pattern = Pattern.compile("[0-9]*"); 	//normal expression
	    return pattern.matcher(str).matches();    
	 } 
	
	//generate script.js
	private void output2js(/*HashMap<String, ArrayList<ArrayList<Object>>> CanvasMap*/){		
		//write script	
		try{
		FileOutputStream script_fos = new FileOutputStream("script.js");
		OutputStreamWriter script_dos = new OutputStreamWriter(script_fos);
		BufferedWriter script = new BufferedWriter(script_dos);
		writeScriptData(script/*, CanvasMap*/);
			//copy script_base.js to script.js
			try{
				FileInputStream base_script_fis = new FileInputStream("base_script.js");
				InputStreamReader base_script_dis = new InputStreamReader(base_script_fis);
				BufferedReader base_script = new BufferedReader(base_script_dis);
				String s;
				while ((s = base_script.readLine()) != null){
					script.write(s+"\n");
				}
				base_script_dis.close();
			} catch(FileNotFoundException e){
				System.out.println("Error: cannot open base_script.js");
			}
			catch(IOException e){
				System.out.println(e);
			}
		script.flush();			//flush buffer
		script.close();
		
		}catch(FileNotFoundException e){
			System.out.println("Error: cannot open script.js for writing.");
		}
		catch(IOException e){
			System.out.println("Error: cannot write to script.js");
		}
	}
	
	//interface for outer class
	public HashMap<String, ArrayList<ArrayList<Object>>> getCanvasMap(){
		return CanvasMap;
	}
}


